package com.gkzf.ai.module.crm.util.assembly;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.gkzf.ai.module.crm.controller.admin.creditReport.vo.admittancecondition.OverdueRecordVo;
import com.gkzf.ai.module.crm.controller.admin.creditReport.vo.creditreportdetails.CreditReportDetailsSaveReqVO;
import com.gkzf.ai.module.crm.dal.dataobject.creditReport.CreditReportBasicDO;
import com.gkzf.ai.module.crm.dal.dataobject.creditReport.UserCreditReportAdmittanceConditionDO;
import com.gkzf.ai.module.crm.enums.ModelAnalysisDictEnum;
import com.gkzf.ai.module.system.api.dict.DictDataApi;
import com.gkzf.ai.module.system.api.dict.dto.DictDataRespDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;

/**
 * 解析全量大模型返回的json格式征信报告对象
 */
@Slf4j
public class AssemblyCreditReportDetails {

    // 定义Map中数据键的常量，以及其他可能的魔法值
    public static final String KEY_MODEL_LOAN = "modelLoan"; // 贷款模型数据键
    public static final String KEY_MODEL_CREDIT_CARD = "modelCreditCard"; // 信用卡模型数据键
    public static final String KEY_QUERY_RECORD = "queryRecord"; // 查询记录数据键
    public static final String KEY_BASIC = "basic"; // 基本信息数据键
    public static final String JSON_RESULT_KEY = "result"; // JSON中包含实际结果的键
    public static final String ERROR_ACCOUNTS_KEY = "异常状态账户列表"; // 异常状态账户列表的键 (假设这个键是固定的)

    // 贷款/信用卡类型的常量 (假设 OverdueRecordSummary 使用这些常量)
    public static final String LOANS = "loans"; // 贷款类型代码 (请确保与实际常量定义匹配)
    public static final String CREDIT_CARD = "creditCard"; // 信用卡类型代码 (请确保与实际常量定义匹配)

    /**
     * 字典api
     */
    private final DictDataApi dictDataApi;

    public AssemblyCreditReportDetails(DictDataApi dictDataApi) {
        this.dictDataApi = dictDataApi;
    }


    /**
     * 根据一个包含JSON数据源的Map，计算并填充各种征信报告统计和详情。
     *
     * @param financeMap              包含按数据类型（如贷款、信用卡等）键控的JSON字符串的Map。
     * @param overdueMap              用于填充逾期记录汇总信息（按类型分组）的Map。
     * @param detailsDO               用于填充详细信息的目录对象。
     * @param creditReportBasicDO     用于填充基本征信信息的目录对象。
     * @param admittanceConditionDO   用于填充准入条件信息的目录对象。
     */
    public void calculateOverdueStatistics(Map<String, Object> financeMap,
                                           Map<String, List<OverdueRecordVo>> overdueMap,
                                           CreditReportDetailsSaveReqVO detailsDO,
                                           CreditReportBasicDO creditReportBasicDO,
                                           UserCreditReportAdmittanceConditionDO admittanceConditionDO) {

        // 输入参数Map和目标对象的空值基本检查
        if (Objects.isNull(financeMap)) {
            log.warn("征信解析大模型数据：输入 financeMap 为 null，跳过计算。");
            return;
        }
        // 记录警告如果目标对象为空，因为数据将不会被填充
        if (Objects.isNull(detailsDO)) log.warn("征信解析大模型数据：目标 detailsDO 为 null。部分数据将无法填充。");
        if (Objects.isNull(creditReportBasicDO)) log.warn("征信解析大模型数据：目标 creditReportBasicDO 为 null。基本信息将无法填充。");
        if (Objects.isNull(admittanceConditionDO)) log.warn("征信解析大模型数据：目标 admittanceConditionDO 为 null。准入条件将无法填充。");


        // 处理 "modelLoan" 数据
        processFinanceData(financeMap, KEY_MODEL_LOAN, map -> {
            if (Objects.nonNull(detailsDO)) { // 仅在 detailsDO 可用时处理
                log.debug("征信解析大模型数据：正在为 detailsDO 处理贷款数据...");
                // 逾期明细
                new OverdueInfoDetails().getCreditReportLoanOverdue(map, detailsDO); // 假设这些辅助类是无状态的
                // 征信信息 (此注释可能与键的实际含义略有偏差，但保留原逻辑)
                new CreditReportInfo().getCreditReportInfo(map, detailsDO);
                // 贷款未结清账户明细列表
                new LoanAccountDetails().getLoanAccountDetails(map, detailsDO);

                // 处理异常状态账户
                Object errorAccountsObj = map.get(ERROR_ACCOUNTS_KEY);
                if (errorAccountsObj instanceof JSONArray errorJsonArray) { // 安全地检查类型
                    if (!errorJsonArray.isEmpty()){
                        log.info("征信解析大模型数据：从 '{}' 获取异常状态列表: {}", KEY_MODEL_LOAN, errorJsonArray);
                        detailsDO.setErrorJson(errorJsonArray.toJSONString());
                    }
                } else if (Objects.nonNull(errorAccountsObj)) {
                    log.warn("征信解析大模型数据：键 '{}' 在 '{}' 中期望得到 JSONArray 类型，但实际为 {}。", ERROR_ACCOUNTS_KEY, KEY_MODEL_LOAN, errorAccountsObj.getClass().getName());
                }
            }
            if (Objects.nonNull(overdueMap)) { // 仅在 overdueMap 可用时处理
                log.debug("征信解析大模型数据：正在为 overdueMap 处理贷款数据...");
                // 逾期汇总 (假设 overdueMap 被传递给辅助方法)
                new OverdueRecordSummary().getOverdueRecordLoansVoList(map, overdueMap, LOANS,null);
            }
        });

        // 处理 "modelCreditCard" 数据
        processFinanceData(financeMap, KEY_MODEL_CREDIT_CARD, map -> {
            if (Objects.nonNull(detailsDO)) { // 仅在 detailsDO 可用时处理
                log.debug("征信解析大模型数据：正在为 detailsDO 处理信用卡数据...");
                // 逾期明细 (注意: 与贷款逾期使用了相同的方法 - 验证是否正确)
                new OverdueInfoDetails().getCreditReportLoanOverdue(map, detailsDO);
                // 信用卡 未结清账户明细列表
                new CreditAccountDetails().getCreditAccountDetails(map, detailsDO);
            }
            if (Objects.nonNull(overdueMap)) { // 仅在 overdueMap 可用时处理
                log.debug("征信解析大模型数据：正在为 overdueMap 处理信用卡数据...");
                // 逾期汇总 (注意: 与贷款汇总使用了相同的方法 - 验证是否正确)
                new OverdueRecordSummary().getOverdueRecordLoansVoList(map, overdueMap, CREDIT_CARD,null);
            }
        });

        // 处理 "queryRecord" 数据
        processFinanceData(financeMap, KEY_QUERY_RECORD, map -> {
            if (Objects.nonNull(admittanceConditionDO) && Objects.nonNull(detailsDO)) { // 检查两个必要的目标DO对象
                log.debug("征信解析大模型数据：正在为 admittanceConditionDO 和 detailsDO 处理查询记录数据...");
                // 假设 dictDataApi 已注入或可通过其他方式访问
                // 如果它是注入的依赖，可能需要将其传递给助手方法
                List<DictDataRespDTO> queryLimitDictDataList = dictDataApi.getDictDataList(ModelAnalysisDictEnum.QUERY_RECORD_COLLECT.getCode());
                // 构造方法的方式似乎是有状态的，所以保持局部创建
                new AssembleQueryRecordDetails(queryLimitDictDataList).getAssembleQueryRecordDetails(map, admittanceConditionDO, detailsDO);
            }
        });

        // 处理 "basic" 数据
        processFinanceData(financeMap, KEY_BASIC, map -> {
            if (Objects.nonNull(creditReportBasicDO) && Objects.nonNull(detailsDO)) { // 检查两个必要的目标DO对象
                log.debug("征信解析大模型数据：正在为 creditReportBasicDO 和 detailsDO 处理基本信息数据...");
                // 征信基本信息
                new AssembleBasicInfo().assemblyBasicInfo(map, creditReportBasicDO, detailsDO);
                // 循环贷一 二 非循环贷 贷记卡信息汇总
                new LoanSummary().getLoanSummary(map, detailsDO);
                // 公共信息汇总
                new PublicInformationSummary().getPublicInformationSummary(map, detailsDO);
            }
        });
    }

    /**
     * 安全地检索、解析并处理特定的金融数据条目的助手方法。
     * 封装了JSON解析和错误处理。
     *
     * @param financeMap 源Map。
     * @param key        数据条目的键（例如，"modelLoan"）。
     * @param processor  一个 Consumer lambda，接受解析后的 Map<String, Object> 结果，
     *                   并使用目标DO对象（可从外部方法作用域访问）执行必要的处理逻辑。
     */
    private void processFinanceData(Map<String, Object> financeMap, String key, Consumer<Map<String, Object>> processor) {
        String jsonString = (String) financeMap.get(key);

        // 检查键是否存在且值非空字符串
        if (Objects.isNull(jsonString) || !StringUtils.hasText(jsonString)) { // 使用 Spring 的 hasText 方法增强健壮性
            if (financeMap.containsKey(key)) { // 记录警告如果键存在但值为空或空白字符串
                log.warn("征信解析大模型数据：Finance map 中键 '{}' 存在但其值为 null 或空白字符串。", key);
            } else { // 记录（调试级别）如果键完全缺失
                log.debug("征信解析大模型数据：Finance map 中键 '{}' 未找到。", key);
            }
            return; // 跳过处理如果未找到有效的JSON字符串
        }

        try {
            JSONObject jsonObject = JSON.parseObject(jsonString);

            // 检查外部JSONObject是否解析成功并包含'result'键
            if (Objects.isNull(jsonObject) || !jsonObject.containsKey(JSON_RESULT_KEY)) {
                log.warn("征信解析大模型数据：Finance map 中键 '{}' 对应的 JSON 无效或不包含期望的 '{}' 键: {}", key, JSON_RESULT_KEY, jsonString);
                return; // 跳过如果未找到内部结果JSON
            }

            String resultJsonString = (String) jsonObject.get(JSON_RESULT_KEY);
            if (Objects.isNull(resultJsonString) || !StringUtils.hasText(resultJsonString)) {
                log.warn("征信解析大模型数据：Finance map 中键 '{}' 的 '{}' 值为 null 或空白字符串。", key, JSON_RESULT_KEY);
                return; // 跳过如果内部结果JSON字符串无效
            }

            Map<String, Object> resultMap = JSON.parseObject(resultJsonString, new TypeReference<>() {});

            // 检查内部结果Map是否解析成功
            if (Objects.nonNull(resultMap)) {
                // 使用解析后的Map执行提供的处理逻辑
                log.debug("征信解析大模型数据：成功解析键 '{}' 对应的数据，继续执行处理逻辑。", key);
                processor.accept(resultMap);
            } else {
                log.warn("征信解析大模型数据：无法解析键 '{}' 对应的内部结果 JSON: {}", key, resultJsonString);
            }

        } catch (Exception e) { // 捕获广泛异常以处理潜在的JSON解析问题或处理逻辑中的其他运行时错误
            log.error("征信解析大模型数据：处理 finance map 中键 '{}' 时发生错误，原始 JSON 字符串: {}", key, jsonString, e);
            // 根据需求，你可能希望在这里抛出自定义异常
        }
    }

    // 假设 dictDataApi 是一个字段或可访问的静态工具类
    // private DictDataApi dictDataApi; // 示例注入字段

    // ... 其他方法 ...
    /**
     * 汇总某个特定类别的逾期统计数据到单个 OverdueRecordVo 对象。
     * 假设输入的 list 包含的条目都属于同一个类别（例如，所有键都以 "1:" 开头）。
     *
     * @param list                针对某个类别过滤后的 Map.Entry 列表 (例如，键如 "1:suffix", "1:suffix2")
     * @param overdueRecordList   用于添加结果 OverdueRecordVo 对象的列表
     */
    public static void summaryOverdueStatistics(List<Map.Entry<String, Integer>> list, List<OverdueRecordVo> overdueRecordList) {
        // 创建一个新的 OverdueRecordVo 对象来存储当前类别汇总的结果
        OverdueRecordVo overdueRecordVo = new OverdueRecordVo();

        // 遍历列表中的每一个 Map.Entry (例如 "1:continuousOverdueMonths"=10)
        list.forEach(entry -> {
            String key = entry.getKey();
            // 将键按 ":" 分割成类别和后缀
            String[] parts = key.split(":");

            // 添加基本校验：确保键的格式是 "类别:后缀"
            if (parts.length != 2) {
                // 如果格式不正确，记录警告并跳过该条目
                log.info("Warning: Unexpected key format in summaryOverdueStatistics: {}. Expected 'category:suffix'. Skipping.", key);
                return; // 对于 forEach 中的 lambda，使用 return 相当于 continue
            }

            String category = parts[0]; // 获取类别 (例如 "1", "6")
            String suffix = parts[1];   // 获取后缀 (例如 "continuousOverdueMonths")
            Integer value = entry.getValue(); // 获取聚合的数值

            // 构造用于设置 OverdueRecordVo 字段的字符串值 (例如 "1,10")
            String formattedValue = category + "," + value;

            // 使用 switch 语句根据后缀来调用对应的 setter 方法，避免了重复的 if-else if 结构
            switch (suffix) {
                case "maxMonthsOverdueAmountMonths":
                    overdueRecordVo.setMaxMonthsOverdueAmountMonths(formattedValue);
                    break;
                case "overUeRecordMonths":
                    overdueRecordVo.setOverUeRecordMonths(formattedValue);
                    break;
                case "maxMonthsOverdueMonths":
                    overdueRecordVo.setMaxMonthsOverdueMonths(formattedValue);
                    break;
                case "continuousOverdueMonths":
                    overdueRecordVo.setContinuousOverdueMonths(formattedValue);
                    break;
                default:
                    // 如果遇到未知的后缀，记录警告
                    log.info("Warning: Unknown suffix encountered in summaryOverdueStatistics: {} for key: {}. No setter called.", suffix, key);
                    // 或者抛出异常，取决于你的错误处理策略
                    break;
            }
        });
        overdueRecordList.add(overdueRecordVo);
    }

    /**
     * 根据逾期记录生成 OverdueRecordVo 列表。
     * @param overdue 逾期记录的 Map，键为 "类别:字段"，值为数值
     * @return OverdueRecordVo 列表
     */
    public static List<OverdueRecordVo> getOverdueRecordVos(Map<String, Integer> overdue) {
        List<OverdueRecordVo> overdueRecordList = new ArrayList<>();
        // 定义需要处理的类别（时间段）列表
        List<String> categoriesToProcess = List.of("1", "6", "12", "24", "60");

        // 遍历类别列表，按类别过滤 overdue map 并调用 summaryOverdueStatistics
        categoriesToProcess.forEach(category -> {
            // 过滤出键以当前类别开头的条目
            List<Map.Entry<String, Integer>> filteredEntries = overdue.entrySet().stream()
                    // 通过检查键是否以类别和 ':' 开头来过滤
                    .filter(entry -> entry.getKey().startsWith(category + ":"))
                    .toList(); // 使用现代 Java 的 toList() 收集结果

            // 调用现有方法并传入过滤后的列表和结果列表
            summaryOverdueStatistics(filteredEntries, overdueRecordList);
        });
        return overdueRecordList;
    }

    /**
     * 辅助方法：处理单个逾期字段的字符串值，解析并更新聚合 Map。
     * @param valueStr 要处理的字符串值 (例如 "1:10")
     * @param suffix   该字段对应的键后缀 (例如 "continuousOverdueMonths")
     * @param overdueMap 聚合结果 Map
     * @param delimiter 分隔符 (例如 ",")
     * @param type 1 合计 2：取最大值
     */
    public static void processOverdueField(String valueStr, String suffix, Map<String, Integer> overdueMap, String delimiter,String type) {
        // 检查值是否非空
        if (Objects.nonNull(valueStr)) {
            // 按分隔符分割字符串
            String[] split = valueStr.split(delimiter);
            // 基本的格式校验：确保分割后有两个部分
            if (split.length == 2) {
                try {
                    String category = split[0]; // 获取类别（例如 "1", "6"）
                    int value = Integer.parseInt(split[1]); // 获取数值
                    // 构造 map 的键，格式为 "类别:后缀"
                    String key = category + ":" + suffix;

                    // 使用 merge 方法进行简洁的聚合（如果键存在则累加，否则放入新值）
                    // Integer::sum 是合并函数，表示将新值加到旧值上
                    if (type.equals("1")) {
                        overdueMap.merge(key, value, Integer::sum);
                    }
                    if (type.equals("2")) {
                        overdueMap.merge(key, value, Integer::max);
                    }

                } catch (NumberFormatException e) {
                    // 如果需要，处理解析错误（例如，记录警告日志）
                    log.info("Warning: Could not parse integer from value: '{}' for field suffix: '{}'. Error: {}", valueStr, suffix, e.getMessage());
                }
            } else {
                // 如果需要，处理分割错误（例如，记录警告日志）
                log.info("Warning: Unexpected format for value: '{}' for field suffix: '{}'. Expected format 'category{}value'.", valueStr, suffix, delimiter);
            }
        }
    }

}

